package com.heima.juc.unlock;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author : leimingchao
 * @date Date : 2022年02月12日 15:30
 */
@Slf4j(topic = "c.TestAccount4")
public class TestAccount4 {

    public static void main(String[] args) {

        BigDecimal bigDecimal = new BigDecimal("10000");
        DecimalAccount decimalAccount = new DecimalAccountCas(bigDecimal);
        DecimalAccount.demo(decimalAccount);

    }
}

class DecimalAccountCas implements DecimalAccount {

    private AtomicReference<BigDecimal> balance;

    public DecimalAccountCas(BigDecimal balance) {
        this.balance = new AtomicReference<>(balance);
    }

    @Override
    public BigDecimal getBalance() {
        return balance.get();
    }

    @Override
    public void withdraw(BigDecimal amount) {
        while (true) {
            BigDecimal prev = balance.get();

            BigDecimal next = prev.subtract(amount);

            if (balance.compareAndSet(prev, next)) {
                break;
            }
        }
    }
}

interface DecimalAccount {

    /**
     * 获得余额
     * @return
     */
    BigDecimal getBalance();

    /**
     * 取款
     * @param amount
     */
    void withdraw(BigDecimal amount);

    /**
     * 方法内会启动1000个线程，每个线程做 - 10 元的操作
     * 如果初始余额为10000 那么正确的结果应当是 0
     * @param account
     */
    static void demo(DecimalAccount account) {

        List<Thread> ts = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            ts.add(new Thread(() -> {
                account.withdraw(BigDecimal.TEN);
            }));

        }

        LocalDateTime start = LocalDateTime.now();

        ts.forEach(Thread::start);

        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        LocalDateTime end = LocalDateTime.now();

        System.out.println(account.getBalance() + " cost: " + Duration.between(start, end).toMillis() + " ms");
    }
}
